{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# Copyright 2014 Brett Slatkin, Pearson Education Inc.\n",
    "#\n",
    "# Licensed under the Apache License, Version 2.0 (the \"License\");\n",
    "# you may not use this file except in compliance with the License.\n",
    "# You may obtain a copy of the License at\n",
    "#\n",
    "#     http://www.apache.org/licenses/LICENSE-2.0\n",
    "#\n",
    "# Unless required by applicable law or agreed to in writing, software\n",
    "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
    "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
    "# See the License for the specific language governing permissions and\n",
    "# limitations under the License.\n",
    "\n",
    "# Preamble to mimick book environment\n",
    "import logging\n",
    "from pprint import pprint\n",
    "from sys import stdout as STDOUT"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Received: First\n",
      "Received: Second\n"
     ]
    }
   ],
   "source": [
    "# Example 1\n",
    "def my_coroutine():\n",
    "    while True:\n",
    "        received = yield\n",
    "        print('Received:', received)\n",
    "\n",
    "it = my_coroutine()\n",
    "next(it)             # Prime the coroutine\n",
    "it.send('First')\n",
    "it.send('Second')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# Example 2\n",
    "def minimize():\n",
    "    current = yield\n",
    "    while True:\n",
    "        value = yield current\n",
    "        current = min(value, current)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "10\n",
      "4\n",
      "4\n",
      "-1\n"
     ]
    }
   ],
   "source": [
    "# Example 3\n",
    "it = minimize()\n",
    "next(it)            # Prime the generator\n",
    "print(it.send(10))\n",
    "print(it.send(4))\n",
    "print(it.send(22))\n",
    "print(it.send(-1))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# Example 4\n",
    "ALIVE = '*'\n",
    "EMPTY = '-'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# Example 5\n",
    "from collections import namedtuple\n",
    "Query = namedtuple('Query', ('y', 'x'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# Example 6\n",
    "def count_neighbors(y, x):\n",
    "    n_ = yield Query(y + 1, x + 0)  # North\n",
    "    ne = yield Query(y + 1, x + 1)  # Northeast\n",
    "    # Define e_, se, s_, sw, w_, nw ...\n",
    "    e_ = yield Query(y + 0, x + 1)  # East\n",
    "    se = yield Query(y - 1, x + 1)  # Southeast\n",
    "    s_ = yield Query(y - 1, x + 0)  # South\n",
    "    sw = yield Query(y - 1, x - 1)  # Southwest\n",
    "    w_ = yield Query(y + 0, x - 1)  # West\n",
    "    nw = yield Query(y + 1, x - 1)  # Northwest\n",
    "    neighbor_states = [n_, ne, e_, se, s_, sw, w_, nw]\n",
    "    count = 0\n",
    "    for state in neighbor_states:\n",
    "        if state == ALIVE:\n",
    "            count += 1\n",
    "    return count"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "First yield:  Query(y=11, x=5)\n",
      "Second yield: Query(y=11, x=6)\n",
      "...\n",
      "Count:  2\n"
     ]
    }
   ],
   "source": [
    "# Example 7\n",
    "it = count_neighbors(10, 5)\n",
    "q1 = next(it)                  # Get the first query\n",
    "print('First yield: ', q1)\n",
    "q2 = it.send(ALIVE)            # Send q1 state, get q2\n",
    "print('Second yield:', q2)\n",
    "q3 = it.send(ALIVE)            # Send q2 state, get q3\n",
    "print('...')\n",
    "q4 = it.send(EMPTY)\n",
    "q5 = it.send(EMPTY)\n",
    "q6 = it.send(EMPTY)\n",
    "q7 = it.send(EMPTY)\n",
    "q8 = it.send(EMPTY)\n",
    "try:\n",
    "    it.send(EMPTY)     # Send q8 state, retrieve count\n",
    "except StopIteration as e:\n",
    "    print('Count: ', e.value)  # Value from return statement"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# Example 8\n",
    "Transition = namedtuple('Transition', ('y', 'x', 'state'))\n",
    "\n",
    "\n",
    "# Example 9\n",
    "def game_logic(state, neighbors):\n",
    "    pass\n",
    "\n",
    "def step_cell(y, x):\n",
    "    state = yield Query(y, x)\n",
    "    neighbors = yield from count_neighbors(y, x)\n",
    "    next_state = game_logic(state, neighbors)\n",
    "    yield Transition(y, x, next_state)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# Example 10\n",
    "def game_logic(state, neighbors):\n",
    "    if state == ALIVE:\n",
    "        if neighbors < 2:\n",
    "            return EMPTY     # Die: Too few\n",
    "        elif neighbors > 3:\n",
    "            return EMPTY     # Die: Too many\n",
    "    else:\n",
    "        if neighbors == 3:\n",
    "            return ALIVE     # Regenerate\n",
    "    return state"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Me:       Query(y=10, x=5)\n",
      "Q1:       Query(y=11, x=5)\n",
      "...\n",
      "Outcome:  Transition(y=10, x=5, state='-')\n"
     ]
    }
   ],
   "source": [
    "# Example 11\n",
    "it = step_cell(10, 5)\n",
    "q0 = next(it)           # Initial location query\n",
    "print('Me:      ', q0)\n",
    "q1 = it.send(ALIVE)     # Send my status, get neighbor query\n",
    "print('Q1:      ', q1)\n",
    "print('...')\n",
    "q2 = it.send(ALIVE)\n",
    "q3 = it.send(ALIVE)\n",
    "q4 = it.send(ALIVE)\n",
    "q5 = it.send(ALIVE)\n",
    "q6 = it.send(EMPTY)\n",
    "q7 = it.send(EMPTY)\n",
    "q8 = it.send(EMPTY)\n",
    "t1 = it.send(EMPTY)     # Send for q8, get game decision\n",
    "print('Outcome: ', t1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# Example 12\n",
    "TICK = object()\n",
    "\n",
    "def simulate(height, width):\n",
    "    while True:\n",
    "        for y in range(height):\n",
    "            for x in range(width):\n",
    "                yield from step_cell(y, x)\n",
    "        yield TICK"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "# Example 13\n",
    "class Grid(object):\n",
    "    def __init__(self, height, width):\n",
    "        self.height = height\n",
    "        self.width = width\n",
    "        self.rows = []\n",
    "        for _ in range(self.height):\n",
    "            self.rows.append([EMPTY] * self.width)\n",
    "\n",
    "    def __str__(self):\n",
    "        output = ''\n",
    "        for row in self.rows:\n",
    "            for cell in row:\n",
    "                output += cell\n",
    "            output += '\\n'\n",
    "        return output\n",
    "\n",
    "# Example 14\n",
    "    def query(self, y, x):\n",
    "        return self.rows[y % self.height][x % self.width]\n",
    "\n",
    "    def assign(self, y, x, state):\n",
    "        self.rows[y % self.height][x % self.width] = state"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# Example 15\n",
    "def live_a_generation(grid, sim):\n",
    "    progeny = Grid(grid.height, grid.width)\n",
    "    item = next(sim)\n",
    "    while item is not TICK:\n",
    "        if isinstance(item, Query):\n",
    "            state = grid.query(item.y, item.x)\n",
    "            item = sim.send(state)\n",
    "        else:  # Must be a Transition\n",
    "            progeny.assign(item.y, item.x, item.state)\n",
    "            item = next(sim)\n",
    "    return progeny"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "---*-----\n",
      "----*----\n",
      "--***----\n",
      "---------\n",
      "---------\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# Example 16\n",
    "grid = Grid(5, 9)\n",
    "grid.assign(0, 3, ALIVE)\n",
    "grid.assign(1, 4, ALIVE)\n",
    "grid.assign(2, 2, ALIVE)\n",
    "grid.assign(2, 3, ALIVE)\n",
    "grid.assign(2, 4, ALIVE)\n",
    "print(grid)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "    0     |     1     |     2     |     3     |     4    \n",
      "---*----- | --------- | --------- | --------- | ---------\n",
      "----*---- | --*-*---- | ----*---- | ---*----- | ----*----\n",
      "--***---- | ---**---- | --*-*---- | ----**--- | -----*---\n",
      "--------- | ---*----- | ---**---- | ---**---- | ---***---\n",
      "--------- | --------- | --------- | --------- | ---------\n"
     ]
    }
   ],
   "source": [
    "# Example 17\n",
    "class ColumnPrinter(object):\n",
    "    def __init__(self):\n",
    "        self.columns = []\n",
    "\n",
    "    def append(self, data):\n",
    "        self.columns.append(data)\n",
    "\n",
    "    def __str__(self):\n",
    "        row_count = 1\n",
    "        for data in self.columns:\n",
    "            row_count = max(row_count, len(data.splitlines()) + 1)\n",
    "        rows = [''] * row_count\n",
    "        for j in range(row_count):\n",
    "            for i, data in enumerate(self.columns):\n",
    "                line = data.splitlines()[max(0, j - 1)]\n",
    "                if j == 0:\n",
    "                    padding = ' ' * (len(line) // 2)\n",
    "                    rows[j] += padding + str(i) + padding\n",
    "                else:\n",
    "                    rows[j] += line\n",
    "                if (i + 1) < len(self.columns):\n",
    "                    rows[j] += ' | '\n",
    "        return '\\n'.join(rows)\n",
    "\n",
    "columns = ColumnPrinter()\n",
    "sim = simulate(grid.height, grid.width)\n",
    "for i in range(5):\n",
    "    columns.append(str(grid))\n",
    "    grid = live_a_generation(grid, sim)\n",
    "\n",
    "print(columns)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['A', 1, 2, 'B']\n"
     ]
    }
   ],
   "source": [
    "# Example 18\n",
    "def delegated():\n",
    "    yield 1\n",
    "    yield 2\n",
    "\n",
    "def composed():\n",
    "    yield 'A'\n",
    "    for value in delegated():  # yield from in Python 3\n",
    "        yield value\n",
    "    yield 'B'\n",
    "\n",
    "print(list(composed()))\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1, 8]\n"
     ]
    }
   ],
   "source": [
    "# Example 19\n",
    "class MyReturn(Exception):\n",
    "    def __init__(self, value):\n",
    "        self.value = value\n",
    "\n",
    "def delegated():\n",
    "    yield 1\n",
    "    raise MyReturn(2)  # return 2 in Python 3\n",
    "    yield 'Not reached'\n",
    "\n",
    "def composed():\n",
    "    try:\n",
    "        for value in delegated():\n",
    "            yield value\n",
    "    except MyReturn as e:\n",
    "        output = e.value\n",
    "    yield output * 4\n",
    "\n",
    "print(list(composed()))\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "  0   |   1   |   2   |   3   |   4  \n",
      "----- | ----- | ----- | ----- | -----\n",
      "-*--- | --*-- | --**- | --*-- | -----\n",
      "--**- | --**- | -*--- | -*--- | -**--\n",
      "---*- | --**- | --**- | --*-- | -----\n",
      "----- | ----- | ----- | ----- | -----\n"
     ]
    }
   ],
   "source": [
    "# Example 20\n",
    "# This is for the introductory diagram\n",
    "grid = Grid(5, 5)\n",
    "grid.assign(1, 1, ALIVE)\n",
    "grid.assign(2, 2, ALIVE)\n",
    "grid.assign(2, 3, ALIVE)\n",
    "grid.assign(3, 3, ALIVE)\n",
    "\n",
    "columns = ColumnPrinter()\n",
    "sim = simulate(grid.height, grid.width)\n",
    "for i in range(5):\n",
    "    columns.append(str(grid))\n",
    "    grid = live_a_generation(grid, sim)\n",
    "\n",
    "print(columns)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.5.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}
